/*
 * http://www.rbgrn.net/content/313-light-racer-3d-days-1-2-learning-opengl-es
 */

package com.android.superobstacleavoider;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

import android.util.Log;

public class ObjImporter {
	private static final String SPACE = " ";
	private static final String SLASH = "/";

	private static boolean m_normalsEnabled;

	public static ModelData importObj(InputStream in) throws Exception {
		m_normalsEnabled = false;
		ModelData data = new ModelData();
		ArrayList<Vector3> verticies = new ArrayList<Vector3>();
		ArrayList<UVCoord> uvs = new ArrayList<UVCoord>();
		ArrayList<Vector3> normals = new ArrayList<Vector3>();
		ArrayList<Face> faces = new ArrayList<Face>();
		// 1) read in verticies,
		// 2) read in uvs
		// 3) create faces which are verticies and uvs expanded
		// 4) unroll faces into ModelData using sequential indicies
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		StringTokenizer st;
		String line = reader.readLine();

		Log.d("ObjectImporter", "Loading obj data");
		while (line != null) {
			st = new StringTokenizer(line, SPACE);
			if (st.countTokens() > 1) {
				String lineType = st.nextToken(SPACE);
				// vertices
				if (lineType.equals("v")) {
					// vertex
					Vector3 vert = new Vector3();
					vert.X = Float.valueOf(st.nextToken());
					vert.Y = Float.valueOf(st.nextToken());
					vert.Z = Float.valueOf(st.nextToken());
					verticies.add(vert);
				}
				// normals
				else if (lineType.equals("vn")) {
					m_normalsEnabled = true;
					Vector3 normal = new Vector3();
					normal.X = Float.valueOf(st.nextToken());
					normal.Y = Float.valueOf(st.nextToken());
					normal.Z = Float.valueOf(st.nextToken());
					normals.add(normal);
				}
				// texture coordinates
				else if (lineType.equals("vt")) {
					// texture mapping
					UVCoord uv = new UVCoord();
					uv.u = Float.valueOf(st.nextToken());
					uv.v = Float.valueOf(st.nextToken());
					uvs.add(uv);
				}
				// faces
				else if (lineType.equals("f")) {
					// face
					Log.d("OBJImporter", "");
					Face face = new Face();
					face.v1 = verticies.get(Integer.valueOf(st.nextToken(SLASH)
							.trim()) - 1);
					if (m_normalsEnabled)
						face.n1 = normals.get(Integer.valueOf(st.nextToken(
								SLASH).trim()) - 1);
					face.uv1 = uvs.get(Integer.valueOf(st.nextToken(SPACE)
							.substring(1)) - 1);

					face.v2 = verticies.get(Integer.valueOf(st.nextToken(SLASH)
							.trim()) - 1);
					if (m_normalsEnabled)
						face.n2 = normals.get(Integer.valueOf(st.nextToken(
								SLASH).trim()) - 1);
					face.uv2 = uvs.get(Integer.valueOf(st.nextToken(SPACE)
							.substring(1)) - 1);

					face.v3 = verticies.get(Integer.valueOf(st.nextToken(SLASH)
							.trim()) - 1);
					if (m_normalsEnabled)
						face.n3 = normals.get(Integer.valueOf(st.nextToken(
								SLASH).trim()) - 1);
					face.uv3 = uvs.get(Integer.valueOf(st.nextToken(SPACE)
							.substring(1)) - 1);

					faces.add(face);
				}
			}

			line = reader.readLine();
		}

		int facesSize = faces.size();
		Log.d("ObjectImporter", facesSize + " polys");
		data.m_vertexCount = facesSize * 3;
		data.m_vertices = new float[facesSize * 3 * 3];
		data.m_normals = new float[facesSize * 3 * 3];
		data.m_texture = new float[facesSize * 3 * 2];
		data.m_indices = new byte[facesSize * 3];

		for (int i = 0; i < facesSize; i++) {
			Face face = faces.get(i);
			data.m_vertices[i * 9] = toFP(face.v1.X);
			data.m_vertices[i * 9 + 1] = toFP(face.v1.Y);
			data.m_vertices[i * 9 + 2] = toFP(face.v1.Z);
			data.m_vertices[i * 9 + 3] = toFP(face.v2.X);
			data.m_vertices[i * 9 + 4] = toFP(face.v2.Y);
			data.m_vertices[i * 9 + 5] = toFP(face.v2.Z);
			data.m_vertices[i * 9 + 6] = toFP(face.v3.X);
			data.m_vertices[i * 9 + 7] = toFP(face.v3.Y);
			data.m_vertices[i * 9 + 8] = toFP(face.v3.Z);

			if (m_normalsEnabled) {
				data.m_normals[i * 9] = toFP(face.n1.X);
				data.m_normals[i * 9 + 1] = toFP(face.n1.Y);
				data.m_normals[i * 9 + 2] = toFP(face.n1.Z);
				data.m_normals[i * 9 + 3] = toFP(face.n2.X);
				data.m_normals[i * 9 + 4] = toFP(face.n2.Y);
				data.m_normals[i * 9 + 5] = toFP(face.n2.Z);
				data.m_normals[i * 9 + 6] = toFP(face.n3.X);
				data.m_normals[i * 9 + 7] = toFP(face.n3.Y);
				data.m_normals[i * 9 + 8] = toFP(face.n3.Z);
			}

			data.m_texture[i * 6] = face.uv1.u;
			data.m_texture[i * 6 + 1] = face.uv1.v;
			data.m_texture[i * 6 + 2] = face.uv2.u;
			data.m_texture[i * 6 + 3] = face.uv2.v;
			data.m_texture[i * 6 + 4] = face.uv3.u;
			data.m_texture[i * 6 + 5] = face.uv3.v;

			data.m_indices[i * 3] = (byte) (i * 3);
			data.m_indices[i * 3 + 1] = (byte) (i * 3 + 1);
			data.m_indices[i * 3 + 2] = (byte) (i * 3 + 2);
		}

		reader.close();
		data.setBuffers();
		return data;
	}

	private static int toFP(float f) {
		// normally you'd << 16 but um, can't do that with a float so we
		// multiply by 16 bits.
		return (int) ((double) f * 0x10000);
	}

	private static class UVCoord {
		public float u;
		public float v;
	}

	private static class Face {
		// texture coordinates
		public UVCoord uv1, uv2, uv3;
		// vertices
		public Vector3 v1, v2, v3;
		// normals
		public Vector3 n1, n2, n3;
	}
}
